package com.koron.operation.classes.form.excel;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Excel坐标计算
 *
 * @author jzp
 * @since 2023/6/2
 */
public class ExcelCoordinateUtil {

    @RequiredArgsConstructor
    @Getter
    public enum ClassesFormCell {
        TITLE(0,0, "标题", "A1"),
        YEAR(2,1, "年份", "B3"),
        MONTH(2,7, "月份", "H3"),
        DAY(3,1, "本月天数", "B4"),
        DATE_ROW(0,1, "日期行", "B7"),
        WEEK_ROW(0,1, "星期行", "B8"),
        SHIFT_ROW(9,0, "排班表行", "A10") // 第一个岗位行
        ;

        private final int rowIndex;
        private final int collIndex;
        private final String name;
        private final String cellIndex;
        @Setter
        private Consumer<Sheet> dealConsumer;

        public ClassesFormCell getCell(int rowIndex, int colIndex) {
            for (ClassesFormCell value : values()) {
                if (hasName(rowIndex, colIndex, value)) {
                    return value;
                }
            }
            return null;
        }

        public boolean hasName(int rowIndex, int colIndex, ClassesFormCell classesFormCell) {
            return rowIndex == classesFormCell.rowIndex && colIndex == classesFormCell.collIndex;
        }
    }

    public static String getStringValue(Sheet sheet, int rowIndex, int colIndex) {
        return cellDecorator(sheet, rowIndex, colIndex, Cell::getStringCellValue);
    }

    public static Object getValue(Sheet sheet, int rowIndex, int colIndex) {
        return cellDecorator(sheet, rowIndex, colIndex, cell -> {
            if (cell.getNumericCellValue() != 0) {
                return cell.getNumericCellValue();
            }  else if (cell.getDateCellValue() != null) {
                return cell.getDateCellValue();
            }
            return cell.getStringCellValue();
        });
    }

    public static Boolean setValue(Sheet sheet, int rowIndex, int colIndex, Object value) {
        return cellDecorator(sheet, rowIndex, colIndex, cell -> {
            setCellValue(cell, value);
            return Boolean.TRUE;
        });
    }

    public static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            return;
        }
        if (value instanceof String) {
            cell.setCellValue((String) value);
        } else if (value instanceof Date) {
            cell.setCellValue((Date) value);
        } else if (value instanceof Double) {
            cell.setCellValue((Double) value);
        } else {
            cell.setCellValue(String.valueOf(value));
        }
    }

    public static CellStyle getCellStyle(Sheet sheet, int rowIndex, int colIndex) {
        return cellDecorator(sheet, rowIndex, colIndex, Cell::getCellStyle);
    }

    public static <T> void rowFill(Sheet sheet, int rowIndex, int colFirstIndex, List<T> data, boolean usePrevStyle) {
        if (sheet == null) {
            return;
        }
        Row row = sheet.getRow(rowIndex);
        Cell prevCell = null;
        // 使用之前单元格样式
        if (usePrevStyle && colFirstIndex - 1 > 0) {
            prevCell = row.getCell(colFirstIndex - 1);
        }
        int colIndex = colFirstIndex;
        for (T datum : data) {
            Cell cell = getOrCreateCell(row, colIndex++);
            setCellValue(cell, datum);
            if (prevCell != null) {
                // cell.setCellType(prevCell.getCellType());
                cell.setCellStyle(prevCell.getCellStyle());
            }
        }

    }

    public static void rowFillNull(Sheet sheet, int rowIndex, int colFirstIndex, int colLastIndex, boolean usePrevStyle) {
        if (sheet == null) {
            return;
        }
        Row row = sheet.getRow(rowIndex);
        Cell prevCell = null;
        // 使用之前单元格样式
        if (usePrevStyle && colFirstIndex - 1 > 0) {
            prevCell = row.getCell(colFirstIndex - 1);
        }
        for (int i = colFirstIndex; i <= colLastIndex; i++) {
            Cell cell = getOrCreateCell(row, i);
            if (prevCell != null) {
                // cell.setCellType(prevCell.getCellType());
                cell.setCellStyle(prevCell.getCellStyle());
            }
        }
    }

    public static <T> T cellDecorator(Sheet sheet, int rowIndex, int colIndex, Function<Cell, T> mapper) {
        return Optional.ofNullable(sheet)
                .map(e -> e.getRow(rowIndex))
                .map(row -> getOrCreateCell(row, colIndex))
                .map(mapper)
                .orElse(null);
    }

    public static Cell getOrCreateCell(Row row, int rowIndex) {
        Cell cell = row.getCell(rowIndex);
        if (cell == null) {
            cell = row.createCell(rowIndex);
        }
        return cell;
    }

    public static Cell createCell(Row row, int rowIndex, CellStyle cellStyle) {
        Cell cell = row.getCell(rowIndex);
        if (cell == null) {
            cell = row.createCell(rowIndex);
            cell.setCellStyle(cellStyle);
        }
        return cell;
    }

    public static void trimRightColumn(Sheet sheet, int removeColumnNum) {
        if (sheet == null || removeColumnNum < 1) {
            return;
        }
        for (Iterator<Row> rowIterator = sheet.rowIterator();
             rowIterator.hasNext(); ) {
            Row row = rowIterator.next();
            for (int i = 31; i > removeColumnNum; i--) {
                Cell cell = row.getCell(i);
                if (cell == null) {
                    continue;
                }
                // 移植样式
                row.getCell(i - 1).setCellStyle(cell.getCellStyle());
                row.removeCell(cell);
            }
        }

        // 移除合并单元格
        List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
        Map<Integer, CellRangeAddress> afterMergedRegions = new HashMap<>();

        for (int i = 0; i < mergedRegions.size(); i++) {
            CellRangeAddress mergedRegion = mergedRegions.get(i);
            if (mergedRegion.getFirstColumn() == 1 && mergedRegion.getLastColumn() == 31) {
                mergedRegion.setLastColumn(removeColumnNum);
                afterMergedRegions.put(i, mergedRegion);
            }
        }
        AtomicInteger i = new AtomicInteger();
        afterMergedRegions.forEach((k,v) -> {
            sheet.removeMergedRegion(k - i.getAndIncrement());
            sheet.addMergedRegionUnsafe(v);
        });
    }

}
